SYSTEMTIME

reversing.kr

reversing.kr is a website containing reverse engineering challenges for different platforms, including Windows, Linux, and .NET. Despite being more than a decade old, many of the challenges don't have English writeups (if they do exist, I might need to grab a copy of this book). I'll be updating this post with writeups for challenges I have found interesting, but wasn't able to find an English writeup for.

PEPassword

We are given 2 executables. Original.exe and Packed.exe. Running Original.exe displays a message box. Original.exe

Running Packed.exe prompts for a password. Packed.exe

Packed.exe starts by loading kernel32.dll and user32.dll. It proceeds by creating a modeless dialog box by calling CreateDialogIndirectParamA and runs a standard message loop. The message loop runs as long as the byte at ss:[ebp + 0x402A3E], which resolves to 0x409410, is 0.

Packed.exe message loop

If we manually set the value at that memory address using a debugger we will eventually reach this interesting set of instructions.

Packed.exe Unpacking

The function at 0x4091DA appears to be some sort of irreversible hashing function. The loop at 0x40921F appears to decrypt whatever is pointed by edi in increments of 4 bytes. What is the value at edi? It's 0x401000, the beginning of the .text segment! If you look closely at the decryption loop, you can make an interesting observation: the decryption relies only on the initial values of eax and ebx, but these values are the result of the hashing function. Luckily, we have access to the unpacked binary, Original.exe. Thus, we know what the first 4 bytes should decrypt to: the first 4 bytes of the unpacked executable!

Unpacked.exe IDA

So we can get the initial value of eax by a simple xor operation. eax = 0x014cec81 ^ 0xb6e62e17. Where 0x014cec81 are the first 4 bytes of the .text segment of the unpacked binary, and 0xb6e62e17 are the bytes of the packed binary. Getting the value of ebx won't be as easy, but we are in luck again, we have the power of technology: the C compiler (or any other programming or scripting language of your choice).

Technology

Code

Since ebx is constrained by its effective width (0xffffffff), we can write a simple program imitating the decryption loop to determine which values of ebx result in correct decryption on the second iteration.

uint32_t rol(uint32_t value, uint8_t shift) {
  return (value << shift) | (value >> (32 - shift));
}

uint32_t ror(uint32_t value, uint8_t shift) {
  return (value >> shift) | (value << (32 - shift));
}

int main() {
  uint32_t eax1 = 0x014cec81 ^ 0xb6e62e17; // eax in first iteration
  uint32_t eax2 = 0x57560000 ^ 0x0d0c7e05; // desired eax in second iteration
  printf("eax %#x\n", eax1);

  for (uint32_t i = 0; i < 0xffffffff; i++) {
    uint32_t eax = eax1;
    uint32_t ebx = i;

    ebx = rol(ebx, eax & 0xff);
    eax ^= ebx;
    eax = ror(eax, (ebx & 0x0000ff00) >> 8);
    ebx = (ebx + eax) & 0xffffffff;

    if (eax == eax2) {
      printf("ebx: %#x\n", i);
    }
  }
}

Running the code gives us the following output:

Code output

We found 2 potential ebx values. Using a debugger, we can place a breakpoint at the decryption loop and manually set the registers to the found values. Trying the first ebx value doesn't yield anything, as the program crashes from an illegal instruction in the .text section. The second value however gets us the flag!

Flag